Skip to content

✨(backend) add limit on distinct reactions per comment#1978

Open
maboukerfa wants to merge 2 commits intosuitenumerique:mainfrom
maboukerfa:feat/add-max-reaction
Open

✨(backend) add limit on distinct reactions per comment#1978
maboukerfa wants to merge 2 commits intosuitenumerique:mainfrom
maboukerfa:feat/add-max-reaction

Conversation

@maboukerfa
Copy link
Copy Markdown
Contributor

Purpose

The current implementation allows users to add an unlimited number of reactions per comment.
Because the /threads endpoint is called on document load, having multiple comments with
thousands of reactions each could significantly impact page load performance.

Screenshot 2026-03-11 at 00 16 56

Proposal

Implement a configurable limit (default: 15) on the number of distinct emoji reactions per comment.

  • Backend validation ensures the limit cannot be exceeded via API
  • Frontend disables reaction buttons when limit is reached
Screen.Recording.2026-03-11.at.00.21.53.mov

@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch 4 times, most recently from 0399104 to af0eeff Compare March 10, 2026 23:30
@lunika lunika requested review from AntoLC and lunika March 11, 2026 07:05
Copy link
Copy Markdown
Member

@lunika lunika left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only focus on the backend part.

If possible, can you split in 2 commits ? A first one for the backend and a second one for the frontend ?

Thank you so much for your contribution

Comment thread src/backend/core/api/viewsets.py
Comment thread src/backend/core/tests/documents/test_api_documents_comments.py
@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch from af0eeff to 390085d Compare March 11, 2026 21:57
@maboukerfa maboukerfa requested a review from lunika March 11, 2026 22:02
Comment thread src/backend/core/tests/documents/test_api_documents_comments.py Outdated
Comment thread src/backend/core/tests/documents/test_api_documents_comments.py Outdated
@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch from 390085d to 0356763 Compare April 24, 2026 15:17
@maboukerfa maboukerfa requested a review from lunika April 24, 2026 15:18
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new setting REACTIONS_MAX_PER_COMMENT (default 15) and exposes it at the backend /api/v1.0/config/ endpoint. Backend comment viewset now prefetches reactions__users and enforces the maximum number of distinct emoji reactions per comment on POST, returning 400 when exceeded. Adds ReactionFactory.generate_emojis, tests that verify the reaction-limit behavior, and updates frontend types and comment-store logic to receive and enforce the runtime limit from config.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • lunika
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a limit on distinct reactions per comment, which is the core feature across all modified files.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose of limiting reactions and proposing both backend and frontend implementation details.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Around line 12-14: The CHANGELOG.md is missing a blank line before the "###
Changed" heading which triggers markdownlint MD022; insert a single blank line
immediately above the "### Changed" heading (the block containing "### Changed"
and the list item "- ✨(backend) add limit on distinct reactions per comment
`#1978`") so the heading is separated from the previous paragraph.

In `@src/backend/core/api/viewsets.py`:
- Around line 2781-2805: The POST path is vulnerable to TOCTOU: acquire a DB
transaction and lock the parent comment row with select_for_update before
re-checking reactions and creating the Reaction; specifically wrap the logic
around the existing comment variable in an atomic transaction (using Django's
transaction.atomic()) and re-fetch the comment with
Comment.objects.select_for_update().get(pk=comment.pk), then perform the
existence check (models.Reaction.objects.filter(comment=comment,
emoji=emoji).exists()), re-evaluate comment.reactions.count() against
settings.REACTIONS_MAX_PER_COMMENT, and only then call
models.Reaction.objects.get_or_create(...) so the count/enforce-check and create
are done under the same lock.

In `@src/backend/impress/settings.py`:
- Around line 185-189: The REACTIONS_MAX_PER_COMMENT setting currently allows
negative or zero values; add a startup validation immediately after the
REACTIONS_MAX_PER_COMMENT declaration in src/backend/impress/settings.py that
reads the resolved integer and raises a clear configuration error (e.g.,
ValueError or Django's ImproperlyConfigured) if the value is less than 1 so the
process fails fast on bad envs like REACTIONS_MAX_PER_COMMENT=-1; reference the
REACTIONS_MAX_PER_COMMENT symbol and include the environ_name in the error
message for clarity.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStoreAuth.tsx`:
- Around line 6-10: The constructor default for maxReactions in
DocsThreadStoreAuth is out of sync with the backend (10 vs backend 15); update
the constructor parameter default (private readonly maxReactions: number = 10)
to match the backend default 15 or remove the inline default so callers (e.g.,
useComments which passes config?.REACTIONS_MAX_PER_COMMENT) always control the
value; locate the DocsThreadStoreAuth constructor and change the default
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 113c97ad-f0ef-4f98-89da-f86045632297

📥 Commits

Reviewing files that changed from the base of the PR and between e747e03 and 0356763.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/backend/core/api/viewsets.py
  • src/backend/core/factories.py
  • src/backend/core/tests/documents/test_api_documents_comments.py
  • src/backend/impress/settings.py
  • src/frontend/apps/impress/src/core/config/api/useConfig.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStoreAuth.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts

Comment thread CHANGELOG.md Outdated
Comment on lines 2781 to 2805
if request.method == "POST":
emoji = serializer.validated_data["emoji"]

if (
not models.Reaction.objects.filter(
comment=comment, emoji=emoji
).exists()
and comment.reactions.count() >= settings.REACTIONS_MAX_PER_COMMENT
):
return drf.response.Response(
{
"emoji": [
_(
"A comment can have a maximum of %(max)d distinct reactions."
)
% {"max": settings.REACTIONS_MAX_PER_COMMENT}
]
},
status=status.HTTP_400_BAD_REQUEST,
)

reaction, created = models.Reaction.objects.get_or_create(
comment=comment,
emoji=serializer.validated_data["emoji"],
emoji=emoji,
)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Serialize the limit check with the create step.

This is still a TOCTOU race: two concurrent POSTs with different new emojis can both observe count() < REACTIONS_MAX_PER_COMMENT and both create, leaving the comment above the configured cap. Since the backend limit is the main point of this PR, the POST path needs a transaction + row lock on the parent comment before the existence/count/create sequence.

Suggested fix
         if request.method == "POST":
             emoji = serializer.validated_data["emoji"]
-
-            if (
-                not models.Reaction.objects.filter(
-                    comment=comment, emoji=emoji
-                ).exists()
-                and comment.reactions.count() >= settings.REACTIONS_MAX_PER_COMMENT
-            ):
-                return drf.response.Response(
-                    {
-                        "emoji": [
-                            _(
-                                "A comment can have a maximum of %(max)d distinct reactions."
-                            )
-                            % {"max": settings.REACTIONS_MAX_PER_COMMENT}
-                        ]
-                    },
-                    status=status.HTTP_400_BAD_REQUEST,
-                )
-
-            reaction, created = models.Reaction.objects.get_or_create(
-                comment=comment,
-                emoji=emoji,
-            )
+            with transaction.atomic():
+                comment = models.Comment.objects.select_for_update().get(pk=comment.pk)
+                reactions = models.Reaction.objects.filter(comment=comment)
+
+                if (
+                    not reactions.filter(emoji=emoji).exists()
+                    and reactions.count() >= settings.REACTIONS_MAX_PER_COMMENT
+                ):
+                    return drf.response.Response(
+                        {
+                            "emoji": [
+                                _(
+                                    "A comment can have a maximum of %(max)d distinct reactions."
+                                )
+                                % {"max": settings.REACTIONS_MAX_PER_COMMENT}
+                            ]
+                        },
+                        status=status.HTTP_400_BAD_REQUEST,
+                    )
+
+                reaction, created = models.Reaction.objects.get_or_create(
+                    comment=comment,
+                    emoji=emoji,
+                )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/core/api/viewsets.py` around lines 2781 - 2805, The POST path is
vulnerable to TOCTOU: acquire a DB transaction and lock the parent comment row
with select_for_update before re-checking reactions and creating the Reaction;
specifically wrap the logic around the existing comment variable in an atomic
transaction (using Django's transaction.atomic()) and re-fetch the comment with
Comment.objects.select_for_update().get(pk=comment.pk), then perform the
existence check (models.Reaction.objects.filter(comment=comment,
emoji=emoji).exists()), re-evaluate comment.reactions.count() against
settings.REACTIONS_MAX_PER_COMMENT, and only then call
models.Reaction.objects.get_or_create(...) so the count/enforce-check and create
are done under the same lock.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lunika, this is prone to race condition, a user can create a MAX+1 emoji if he spams emojis, is it worth fixing or it's an overkill ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's overkill and I'm not ok to add a lock on the table.

Comment thread src/backend/impress/settings.py
@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch from 0356763 to af1448b Compare April 24, 2026 16:03
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/backend/core/api/viewsets.py (1)

2783-2803: ⚠️ Potential issue | 🔴 Critical

TOCTOU race between the existence/count check and get_or_create.

Two concurrent POSTs with different new emojis can both observe count() < REACTIONS_MAX_PER_COMMENT and both succeed, pushing the comment above the configured cap. Since the whole point of this PR is backend enforcement, the check + create should be serialized via transaction.atomic() + select_for_update() on the parent comment (as already proposed in an earlier review). This comment has already been raised on a prior commit.

Additionally, once you're inside the atomic block, you can reuse the prefetched comment.reactions.all() via len(...) instead of a second COUNT query.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/core/api/viewsets.py` around lines 2783 - 2803, Wrap the
existence/count check and the get_or_create call in a single transaction and
lock the parent comment row to prevent TOCTOU races: start a
transaction.atomic() and re-fetch the Comment with select_for_update() (use the
same Comment instance used by the view), then check the number of distinct
reactions using the prefetched comment.reactions.all() (use len(...) instead of
.count()) and perform Reaction.objects.get_or_create(...) inside that atomic
block; update the code paths around the POST handling in the viewset so the
existence check and creation are serialized on the locked comment to enforce
settings.REACTIONS_MAX_PER_COMMENT.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/backend/core/tests/documents/test_api_documents_comments.py`:
- Around line 881-900: The test test_create_reaction_exceeds_maximum claims
"here we set it to 10" but never overrides settings.REACTIONS_MAX_PER_COMMENT;
update the test to explicitly set the limit using the provided settings fixture
(e.g. assign settings.REACTIONS_MAX_PER_COMMENT = 10 before creating reactions)
so the test adds exactly 10 distinct reactions and then verifies behavior, or
alternatively change the docstring to reflect that it uses the default setting;
modify the test logic in test_create_reaction_exceeds_maximum to reference the
overridden value consistently.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`:
- Line 29: The code currently uses config?.REACTIONS_MAX_PER_COMMENT ?? 0 which
sets max reactions to 0 while config is still loading and prevents adding new
distinct emoji; change the fallback to the backend default (15) or gate
rendering until config is defined: update the expression to
config?.REACTIONS_MAX_PER_COMMENT ?? 15 (or wrap the reactions UI in a check
like if (config === undefined) show loading) and ensure callers such as
canAddReaction(comment, maxPerComment) receive the non-zero default or a
not-ready state.

---

Duplicate comments:
In `@src/backend/core/api/viewsets.py`:
- Around line 2783-2803: Wrap the existence/count check and the get_or_create
call in a single transaction and lock the parent comment row to prevent TOCTOU
races: start a transaction.atomic() and re-fetch the Comment with
select_for_update() (use the same Comment instance used by the view), then check
the number of distinct reactions using the prefetched comment.reactions.all()
(use len(...) instead of .count()) and perform
Reaction.objects.get_or_create(...) inside that atomic block; update the code
paths around the POST handling in the viewset so the existence check and
creation are serialized on the locked comment to enforce
settings.REACTIONS_MAX_PER_COMMENT.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d3574e2-bf82-4ae5-a5c3-064e079f28e9

📥 Commits

Reviewing files that changed from the base of the PR and between 0356763 and af1448b.

📒 Files selected for processing (5)
  • src/backend/core/api/viewsets.py
  • src/backend/core/tests/documents/test_api_documents_comments.py
  • src/frontend/apps/impress/src/core/config/api/useConfig.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStoreAuth.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts

Comment thread src/backend/core/tests/documents/test_api_documents_comments.py
new DocsThreadStoreAuth(
encodeURIComponent(user?.full_name || ''),
canComment,
config?.REACTIONS_MAX_PER_COMMENT ?? 0,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: ?? 0 disables all new reactions until config loads.

When config hasn't resolved yet (first load) or cached config predates this field, REACTIONS_MAX_PER_COMMENT is undefined and this falls back to 0, which gates canAddReaction(..., undefined) to false and blocks any new distinct emoji. This is briefly user-visible because useQuery seeds initialData from localStorage before the refetch completes.

Consider using the backend's documented default (15) as the fallback, or rendering the picker as loading until config is defined:

💡 Optional refactor
-        config?.REACTIONS_MAX_PER_COMMENT ?? 0,
+        config?.REACTIONS_MAX_PER_COMMENT ?? 15,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
config?.REACTIONS_MAX_PER_COMMENT ?? 0,
config?.REACTIONS_MAX_PER_COMMENT ?? 15,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`
at line 29, The code currently uses config?.REACTIONS_MAX_PER_COMMENT ?? 0 which
sets max reactions to 0 while config is still loading and prevents adding new
distinct emoji; change the fallback to the backend default (15) or gate
rendering until config is defined: update the expression to
config?.REACTIONS_MAX_PER_COMMENT ?? 15 (or wrap the reactions UI in a check
like if (config === undefined) show loading) and ensure callers such as
canAddReaction(comment, maxPerComment) receive the non-zero default or a
not-ready state.

@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch from af1448b to c0cf107 Compare April 28, 2026 13:14
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts (1)

31-31: ⚠️ Potential issue | 🟡 Minor

Don't fall back to 0 before config loads.

When /config is still unresolved, or docs_config was cached before this field existed, 0 disables every new distinct emoji until the refetch completes. Use the backend default (15) or keep the picker disabled until config is ready.

Suggested fix
-        config?.REACTIONS_MAX_PER_COMMENT ?? 0,
+        config?.REACTIONS_MAX_PER_COMMENT ?? 15,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`
at line 31, The fallback to 0 for config?.REACTIONS_MAX_PER_COMMENT is disabling
emoji usage before config loads; change the logic in useComments (the place
using config?.REACTIONS_MAX_PER_COMMENT) to avoid returning 0 as the default:
either fall back to the backend default of 15 (e.g., use
config?.REACTIONS_MAX_PER_COMMENT ?? 15) or gate the picker until config is
ready (return undefined/null or a disabled state while config is unresolved),
ensuring REACTIONS_MAX_PER_COMMENT is not forced to 0 before the real config is
available.
src/backend/core/tests/documents/test_api_documents_comments.py (1)

881-901: ⚠️ Potential issue | 🟡 Minor

Pin the reaction cap inside the test.

The docstring says this test sets the limit to 10, but it still inherits whatever settings.REACTIONS_MAX_PER_COMMENT is at runtime. Overriding it via the fixture keeps the test deterministic and shorter.

Suggested fix
 def test_create_reaction_exceeds_maximum(settings):
     """
     Users should not be able to add more than REACTIONS_MAX_PER_COMMENT
-    (here we set it to 10) distinct emoji reactions to a comment.
+    distinct emoji reactions to a comment.
     They should, however, be able to add themselves to an existing reaction.
     """
+    settings.REACTIONS_MAX_PER_COMMENT = 10
+
     user1 = factories.UserFactory()
     user2 = factories.UserFactory()
@@
-    max_reactions = settings.REACTIONS_MAX_PER_COMMENT
+    max_reactions = settings.REACTIONS_MAX_PER_COMMENT
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/core/tests/documents/test_api_documents_comments.py` around lines
881 - 901, The test test_create_reaction_exceeds_maximum relies on
settings.REACTIONS_MAX_PER_COMMENT at runtime; pin this cap inside the test to
make it deterministic by setting a local constant (e.g. MAX_REACTIONS = 10) and
use that value when generating emojis and asserting behavior instead of reading
settings.REACTIONS_MAX_PER_COMMENT, or explicitly override the Django setting
for the test (e.g. with monkeypatch or override_settings) to set
REACTIONS_MAX_PER_COMMENT = 10 before calling
factories.ReactionFactory.generate_emojis and the rest of the test logic so the
test always runs with the intended limit.
src/backend/impress/settings.py (1)

193-197: ⚠️ Potential issue | 🟡 Minor

Reject non-positive REACTIONS_MAX_PER_COMMENT at startup.

0 or negative values silently disable every new distinct reaction in both the API and the UI. Please fail fast with a clear configuration error instead of changing behavior at runtime.

Suggested guard
     def post_setup(cls):
         """Post setup configuration.
         This is the place where you can configure settings that require other
         settings to be loaded.
         """
         super().post_setup()
+
+        if cls.REACTIONS_MAX_PER_COMMENT < 1:
+            raise ValueError(
+                "REACTIONS_MAX_PER_COMMENT must be >= 1 "
+                "(env: REACTIONS_MAX_PER_COMMENT)."
+            )
 
         # The SENTRY_DSN setting should be available to activate sentry for an environment
         if cls.SENTRY_DSN is not None:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/impress/settings.py` around lines 193 - 197, The
REACTIONS_MAX_PER_COMMENT setting currently allows 0 or negative values which
silently disable reactions; add a startup validation right after the
REACTIONS_MAX_PER_COMMENT definition that checks its resolved integer is > 0 and
raises an explicit configuration error if not (e.g. raise
django.core.exceptions.ImproperlyConfigured or ValueError) with a clear message
referencing REACTIONS_MAX_PER_COMMENT and the environment variable
REACTIONS_MAX_PER_COMMENT (environ_name) so misconfiguration fails fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/backend/core/tests/documents/test_api_documents_comments.py`:
- Around line 881-901: The test test_create_reaction_exceeds_maximum relies on
settings.REACTIONS_MAX_PER_COMMENT at runtime; pin this cap inside the test to
make it deterministic by setting a local constant (e.g. MAX_REACTIONS = 10) and
use that value when generating emojis and asserting behavior instead of reading
settings.REACTIONS_MAX_PER_COMMENT, or explicitly override the Django setting
for the test (e.g. with monkeypatch or override_settings) to set
REACTIONS_MAX_PER_COMMENT = 10 before calling
factories.ReactionFactory.generate_emojis and the rest of the test logic so the
test always runs with the intended limit.

In `@src/backend/impress/settings.py`:
- Around line 193-197: The REACTIONS_MAX_PER_COMMENT setting currently allows 0
or negative values which silently disable reactions; add a startup validation
right after the REACTIONS_MAX_PER_COMMENT definition that checks its resolved
integer is > 0 and raises an explicit configuration error if not (e.g. raise
django.core.exceptions.ImproperlyConfigured or ValueError) with a clear message
referencing REACTIONS_MAX_PER_COMMENT and the environment variable
REACTIONS_MAX_PER_COMMENT (environ_name) so misconfiguration fails fast.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`:
- Line 31: The fallback to 0 for config?.REACTIONS_MAX_PER_COMMENT is disabling
emoji usage before config loads; change the logic in useComments (the place
using config?.REACTIONS_MAX_PER_COMMENT) to avoid returning 0 as the default:
either fall back to the backend default of 15 (e.g., use
config?.REACTIONS_MAX_PER_COMMENT ?? 15) or gate the picker until config is
ready (return undefined/null or a disabled state while config is unresolved),
ensuring REACTIONS_MAX_PER_COMMENT is not forced to 0 before the real config is
available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5514d8b4-455b-4654-8039-839a2be32ed4

📥 Commits

Reviewing files that changed from the base of the PR and between af1448b and c0cf107.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/backend/core/api/viewsets.py
  • src/backend/core/factories.py
  • src/backend/core/tests/documents/test_api_documents_comments.py
  • src/backend/impress/settings.py
  • src/frontend/apps/impress/src/core/config/api/useConfig.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStoreAuth.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts

@lunika lunika force-pushed the feat/add-max-reaction branch from c0cf107 to d119fea Compare April 28, 2026 18:48
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/backend/impress/settings.py (1)

193-197: ⚠️ Potential issue | 🟡 Minor

Fail fast on invalid reaction limits.

Line 193-Line 197 currently allows REACTIONS_MAX_PER_COMMENT <= 0. A bad env value can silently disable new reactions. Validate at startup and raise a configuration error early.

Suggested fix
 class Base(Configuration):
@@
     def post_setup(cls):
@@
         super().post_setup()
+        if cls.REACTIONS_MAX_PER_COMMENT < 1:
+            raise ValueError(
+                "REACTIONS_MAX_PER_COMMENT must be >= 1 (env: REACTIONS_MAX_PER_COMMENT)."
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/impress/settings.py` around lines 193 - 197,
REACTIONS_MAX_PER_COMMENT currently permits non-positive values from the
environment which can silently disable reactions; add a startup validation in
settings.py to check that REACTIONS_MAX_PER_COMMENT > 0 (after the IntegerValue
is resolved) and raise a clear configuration error (e.g., ValueError or Django's
ImproperlyConfigured) if the value is <= 0 so invalid env settings fail fast;
update the settings initialization logic around the REACTIONS_MAX_PER_COMMENT
symbol to perform this check and raise with a descriptive message.
src/backend/core/tests/documents/test_api_documents_comments.py (1)

940-959: ⚠️ Potential issue | 🟡 Minor

Align test setup with its documented behavior.

Line 942 says “here we set it to 10”, but the test never overrides the setting; it uses current runtime config instead.

Suggested fix
 def test_create_reaction_exceeds_maximum(settings):
@@
-    user1 = factories.UserFactory()
+    settings.REACTIONS_MAX_PER_COMMENT = 10
+    user1 = factories.UserFactory()
@@
-    max_reactions = settings.REACTIONS_MAX_PER_COMMENT
+    max_reactions = settings.REACTIONS_MAX_PER_COMMENT
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/core/tests/documents/test_api_documents_comments.py` around lines
940 - 959, The test claims "here we set it to 10" but reads
settings.REACTIONS_MAX_PER_COMMENT at runtime; fix by explicitly setting the
limit to 10 for the test (e.g., use Django's override_settings or monkeypatch to
set settings.REACTIONS_MAX_PER_COMMENT = 10) before computing max_reactions and
calling factories.ReactionFactory.generate_emojis; ensure the code that
references max_reactions (the variable max_reactions and the call to
factories.ReactionFactory.generate_emojis) uses this overridden value so the
test matches its docstring.
src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts (1)

31-31: ⚠️ Potential issue | 🟠 Major

Avoid 0 as fallback for max reactions.

Line 31 (config?.REACTIONS_MAX_PER_COMMENT ?? 0) temporarily disables adding new distinct reactions before config resolves (or when cached config predates this field).

Suggested fix
-        config?.REACTIONS_MAX_PER_COMMENT ?? 0,
+        config?.REACTIONS_MAX_PER_COMMENT ?? 15,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`
at line 31, The current fallback of 0 for config?.REACTIONS_MAX_PER_COMMENT
prevents adding reactions until config loads; in useComments.ts replace the
fallback (the expression using REACTIONS_MAX_PER_COMMENT) with a permissive
default so reactions remain allowed (e.g., use config?.REACTIONS_MAX_PER_COMMENT
?? Number.POSITIVE_INFINITY or a sensible constant like DEFAULT_MAX_REACTIONS >
0) and ensure any UI checks treat Infinity/undefined as "no enforced limit".
Update references to REACTIONS_MAX_PER_COMMENT in the useComments hook to use
that new default value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`:
- Line 8: The import ordering in useComments.ts violates the frontend lint rule;
reorder the imports so that external/library imports come first, then absolute
aliases (e.g., '@/core'), and then relative imports used in this file;
specifically adjust the import of useConfig so it appears in the correct group
relative to other imports in the module (locate the import statement "import {
useConfig } from '@/core';" in useComments.ts and move it to the appropriate
position to satisfy the lint-front ordering rules).

---

Duplicate comments:
In `@src/backend/core/tests/documents/test_api_documents_comments.py`:
- Around line 940-959: The test claims "here we set it to 10" but reads
settings.REACTIONS_MAX_PER_COMMENT at runtime; fix by explicitly setting the
limit to 10 for the test (e.g., use Django's override_settings or monkeypatch to
set settings.REACTIONS_MAX_PER_COMMENT = 10) before computing max_reactions and
calling factories.ReactionFactory.generate_emojis; ensure the code that
references max_reactions (the variable max_reactions and the call to
factories.ReactionFactory.generate_emojis) uses this overridden value so the
test matches its docstring.

In `@src/backend/impress/settings.py`:
- Around line 193-197: REACTIONS_MAX_PER_COMMENT currently permits non-positive
values from the environment which can silently disable reactions; add a startup
validation in settings.py to check that REACTIONS_MAX_PER_COMMENT > 0 (after the
IntegerValue is resolved) and raise a clear configuration error (e.g.,
ValueError or Django's ImproperlyConfigured) if the value is <= 0 so invalid env
settings fail fast; update the settings initialization logic around the
REACTIONS_MAX_PER_COMMENT symbol to perform this check and raise with a
descriptive message.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`:
- Line 31: The current fallback of 0 for config?.REACTIONS_MAX_PER_COMMENT
prevents adding reactions until config loads; in useComments.ts replace the
fallback (the expression using REACTIONS_MAX_PER_COMMENT) with a permissive
default so reactions remain allowed (e.g., use config?.REACTIONS_MAX_PER_COMMENT
?? Number.POSITIVE_INFINITY or a sensible constant like DEFAULT_MAX_REACTIONS >
0) and ensure any UI checks treat Infinity/undefined as "no enforced limit".
Update references to REACTIONS_MAX_PER_COMMENT in the useComments hook to use
that new default value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: eb3b8518-189b-4851-b16e-7be870565904

📥 Commits

Reviewing files that changed from the base of the PR and between c0cf107 and d119fea.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/backend/core/api/viewsets.py
  • src/backend/core/factories.py
  • src/backend/core/tests/documents/test_api_documents_comments.py
  • src/backend/impress/settings.py
  • src/frontend/apps/impress/src/core/config/api/useConfig.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStoreAuth.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts

@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch from d119fea to ede3d48 Compare April 29, 2026 16:03
Implement a configurable limit (default: 15) on the number of distinct
emoji reactions per comment.

  - Backend validation ensures the limit cannot be exceeded via API

Signed-off-by: Mohamed El Amine BOUKERFA <boukerfa.ma@gmail.com>
Prevent users from adding more reactions once the per-message
limit has been exceeded. It Disables reaction buttons
when limit is reached

Signed-off-by: Mohamed El Amine BOUKERFA <boukerfa.ma@gmail.com>
@maboukerfa maboukerfa force-pushed the feat/add-max-reaction branch from d73686b to 3be8fc8 Compare April 29, 2026 16:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts (2)

8-8: ⚠️ Potential issue | 🟡 Minor

Reorder imports to match the frontend lint rule.

Line 8 reintroduces the alias-ordering issue (@/core should be grouped before @/cunningham).

🧹 Suggested reorder
+import { useConfig } from '@/core';
 import { useCunninghamTheme } from '@/cunningham';
 import { User, avatarUrlFromName } from '@/features/auth';
 import { useEditorStore } from '@/features/docs/doc-editor/stores';
 import { Doc, useProviderStore } from '@/features/docs/doc-management';
-import { useConfig } from '@/core';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`
at line 8, Reorder the import groups in useComments.ts so alias '@/core' appears
before '@/cunningham' to satisfy the frontend alias-ordering lint rule;
specifically move or reorder the line importing useConfig (import { useConfig }
from '@/core') so it is grouped prior to any imports from '@/cunningham' (and
ensure import grouping/ordering for other aliases follows the same rule).

31-31: ⚠️ Potential issue | 🟡 Minor

Use a non-zero fallback for maxReactions while config is loading.

Line 31 falls back to 0, which temporarily disables adding new distinct reactions before config resolves.

💡 Suggested fix
-        config?.REACTIONS_MAX_PER_COMMENT ?? 0,
+        config?.REACTIONS_MAX_PER_COMMENT ?? 15,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`
at line 31, The fallback for config?.REACTIONS_MAX_PER_COMMENT currently uses 0
which blocks adding reactions while config loads; in useComments.ts change the
fallback to a sensible non-zero default (e.g., DEFAULT_MAX_REACTIONS = 8) and
use that instead of 0 for maxReactions (referencing
config?.REACTIONS_MAX_PER_COMMENT and the maxReactions variable) so distinct
reactions remain enabled until the real config resolves.
src/backend/core/tests/documents/test_api_documents_comments.py (1)

940-943: ⚠️ Potential issue | 🟡 Minor

Make the test setup match the stated scenario (“set to 10”).

Lines 940-943 state a fixed limit of 10, but the test currently uses whatever default is configured at runtime.

✅ Suggested fix
 def test_create_reaction_exceeds_maximum(settings):
     """
     Users should not be able to add more than REACTIONS_MAX_PER_COMMENT
     (here we set it to 10) distinct emoji reactions to a comment.
     They should, however, be able to add themselves to an existing reaction.
     """
+    settings.REACTIONS_MAX_PER_COMMENT = 10
     user1 = factories.UserFactory()
     user2 = factories.UserFactory()
@@
-    max_reactions = settings.REACTIONS_MAX_PER_COMMENT
+    max_reactions = settings.REACTIONS_MAX_PER_COMMENT

Also applies to: 958-959

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/core/tests/documents/test_api_documents_comments.py` around lines
940 - 943, The test asserts a fixed limit of 10 reactions but currently uses the
runtime default; explicitly set the limit to 10 in the test setup by patching
the constant or configuration used by the code under test (e.g., monkeypatch
core.constants.REACTIONS_MAX_PER_COMMENT = 10 or override the app setting the
code reads) before creating reactions, and apply the same explicit override in
the related assertions (the blocks around REACTIONS_MAX_PER_COMMENT usage at the
other affected lines) so the test environment matches the stated scenario.
src/backend/impress/settings.py (1)

193-197: ⚠️ Potential issue | 🟠 Major

Fail fast on invalid REACTIONS_MAX_PER_COMMENT values.

Lines 193-197 accept invalid values (e.g., 0/negative), which silently changes API/UI behavior. Add a startup guard in post_setup to enforce >= 1.

🛡️ Suggested guard
 def post_setup(cls):
     """Post setup configuration.
     This is the place where you can configure settings that require other
     settings to be loaded.
     """
     super().post_setup()
+
+    if cls.REACTIONS_MAX_PER_COMMENT < 1:
+        raise ValueError(
+            "REACTIONS_MAX_PER_COMMENT must be >= 1 (env: REACTIONS_MAX_PER_COMMENT)."
+        )
 
     # The SENTRY_DSN setting should be available to activate sentry for an environment
     if cls.SENTRY_DSN is not None:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/impress/settings.py` around lines 193 - 197, Add a startup guard
in the settings module's post_setup to validate REACTIONS_MAX_PER_COMMENT: read
the configured REACTIONS_MAX_PER_COMMENT value, and if it is less than 1 raise a
clear startup exception (e.g., ValueError or ImproperlyConfigured) with a
message explaining the invalid value and required minimum; reference the
REACTIONS_MAX_PER_COMMENT symbol and implement this check inside the existing
post_setup function so the app fails fast on 0/negative values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/backend/core/factories.py`:
- Around line 239-242: The method generate_emojis currently uses the
module-level fake.unique registry which can leak state between tests; change it
to create and use a fresh Faker instance inside the generate_emojis classmethod
(e.g., instantiate Faker() locally and call its .unique.emoji() to produce n
emojis) so uniqueness is scoped to that instance and does not share global
state—replace uses of the module-level fake.unique in generate_emojis with a
local Faker().unique to isolate/avoid cross-test leakage.

---

Duplicate comments:
In `@src/backend/core/tests/documents/test_api_documents_comments.py`:
- Around line 940-943: The test asserts a fixed limit of 10 reactions but
currently uses the runtime default; explicitly set the limit to 10 in the test
setup by patching the constant or configuration used by the code under test
(e.g., monkeypatch core.constants.REACTIONS_MAX_PER_COMMENT = 10 or override the
app setting the code reads) before creating reactions, and apply the same
explicit override in the related assertions (the blocks around
REACTIONS_MAX_PER_COMMENT usage at the other affected lines) so the test
environment matches the stated scenario.

In `@src/backend/impress/settings.py`:
- Around line 193-197: Add a startup guard in the settings module's post_setup
to validate REACTIONS_MAX_PER_COMMENT: read the configured
REACTIONS_MAX_PER_COMMENT value, and if it is less than 1 raise a clear startup
exception (e.g., ValueError or ImproperlyConfigured) with a message explaining
the invalid value and required minimum; reference the REACTIONS_MAX_PER_COMMENT
symbol and implement this check inside the existing post_setup function so the
app fails fast on 0/negative values.

In
`@src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts`:
- Line 8: Reorder the import groups in useComments.ts so alias '@/core' appears
before '@/cunningham' to satisfy the frontend alias-ordering lint rule;
specifically move or reorder the line importing useConfig (import { useConfig }
from '@/core') so it is grouped prior to any imports from '@/cunningham' (and
ensure import grouping/ordering for other aliases follows the same rule).
- Line 31: The fallback for config?.REACTIONS_MAX_PER_COMMENT currently uses 0
which blocks adding reactions while config loads; in useComments.ts change the
fallback to a sensible non-zero default (e.g., DEFAULT_MAX_REACTIONS = 8) and
use that instead of 0 for maxReactions (referencing
config?.REACTIONS_MAX_PER_COMMENT and the maxReactions variable) so distinct
reactions remain enabled until the real config resolves.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 69004058-0564-42e9-ad3e-5d33d46b5f21

📥 Commits

Reviewing files that changed from the base of the PR and between d119fea and 3be8fc8.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • src/backend/core/api/viewsets.py
  • src/backend/core/factories.py
  • src/backend/core/tests/documents/test_api_documents_comments.py
  • src/backend/core/tests/test_api_config.py
  • src/backend/impress/settings.py
  • src/frontend/apps/impress/src/core/config/api/useConfig.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStoreAuth.tsx
  • src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts

Comment on lines +239 to +242
@classmethod
def generate_emojis(cls, n=10):
"""Generate a list of n unique emojis."""
return [fake.unique.emoji() for _ in range(n)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Avoid shared fake.unique state across test cases.

Line 242 relies on a module-level unique registry, which can leak state between tests and make failures order-dependent.

♻️ Suggested refactor
 `@classmethod`
 def generate_emojis(cls, n=10):
     """Generate a list of n unique emojis."""
-    return [fake.unique.emoji() for _ in range(n)]
+    faker = Faker()
+    return [faker.unique.emoji() for _ in range(n)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@classmethod
def generate_emojis(cls, n=10):
"""Generate a list of n unique emojis."""
return [fake.unique.emoji() for _ in range(n)]
`@classmethod`
def generate_emojis(cls, n=10):
"""Generate a list of n unique emojis."""
faker = Faker()
return [faker.unique.emoji() for _ in range(n)]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend/core/factories.py` around lines 239 - 242, The method
generate_emojis currently uses the module-level fake.unique registry which can
leak state between tests; change it to create and use a fresh Faker instance
inside the generate_emojis classmethod (e.g., instantiate Faker() locally and
call its .unique.emoji() to produce n emojis) so uniqueness is scoped to that
instance and does not share global state—replace uses of the module-level
fake.unique in generate_emojis with a local Faker().unique to isolate/avoid
cross-test leakage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants